<template>
    <!-- 游戏容器的外层 div -->
    <div class="game-container">
      <!-- 显示游戏状态的 div，包括得分、蛇的长度和剩余生命 -->
      <div class="status">
        得分: {{ score }} | 长度: {{ snake.length }} | 剩余生命: <span v-for="n in lives" :key="n">❤️</span>
      </div>
  
      <!-- 游戏设置面板 -->
      <div class="game-settings">
        <div class="difficulty-selector">
          <label>难度：</label>
          <select v-model="gameConfig.difficulty" @change="updateDifficulty">
            <option value="easy">简单</option>
            <option value="medium">中等</option>
            <option value="hard">困难</option>
          </select>
        </div>
        
        <div class="skin-selector">
          <label>皮肤：</label>
          <select v-model="gameConfig.skinType">
            <option value="classic">🐍 经典绿蛇</option>
            <option value="fire">🔥 火焰皮肤</option>
            <option value="rainbow">🌈 彩虹渐变</option>
          </select>
        </div>
      </div>
  
      <!-- 游戏棋盘的 div，根据计算属性设置样式 -->
      <div class="game-board" :style="boardStyle">
        <!-- 循环渲染蛇的每一段 -->
        <div 
          v-for="(segment, index) in snake" 
          :key="index"
          class="snake-segment"
          :style="getSegmentStyle(segment, index)"
        ></div>
        <!-- 显示食物的 div，根据计算属性设置位置 -->
        <div class="food" :style="foodPosition">{{ foodTypes[food.type].emoji }}</div>
      </div>
  
      <!-- 游戏控制按钮的 div -->
      <div class="controls">
        <!-- 开始新游戏的按钮，点击时调用 startGame 方法 -->
        <button @click="startGame" class="start-btn">新游戏</button>
        <!-- 方向键按钮的 div -->
        <div class="arrows">
          <!-- 监听点击事件，当用户点击对应的箭头按钮后，跳转到对应的函数处理 -->
          <button @click="changeDirection('up')" class="arrow-btn">↑</button>
          <div class="horizontal-arrows">
            <button @click="changeDirection('left')" class="arrow-btn">←</button>
            <button @click="changeDirection('right')" class="arrow-btn">→</button>
          </div>
          <button @click="changeDirection('down')" class="arrow-btn">↓</button>
        </div>
      </div>
  
      <!-- 排行榜 -->
      <div class="leaderboard">
        <h3>排行榜</h3>
        <div class="scores-list">
          <div v-for="(score, index) in topScores" :key="index" class="score-item">
            <span class="rank">#{index + 1}</span>
            <span class="score-value">{{ score }}分</span>
          </div>
        </div>
      </div>
    </div>
  </template>
  
  <script>
  // 从 Vue 中导入需要的响应式 API 和生命周期钩子函数
  import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'
  
  export default {
    setup() {
      // ============= 状态声明 =============
      // 棋盘的网格大小，使用 ref 创建响应式数据
      const gridSize = ref(20)
      // 新增游戏配置
      const gameConfig = reactive({
        difficulty: 'medium', // 'easy', 'medium', 'hard'
        skinType: 'classic', // 'classic', 'fire', 'rainbow'
        speedMultiplier: 1,
        baseSpeed: 200
      })
  
      // 排行榜数据
      const leaderboard = ref([])
  
      // 食物类型定义
      const foodTypes = {
        normal: { emoji: '🍎', points: 10, color: 'red', probability: 0.7 },
        speed: { emoji: '🚀', points: 5, color: 'red', probability: 0.1 },
        slow: { emoji: '🛑', points: 5, color: 'blue', probability: 0.1 },
        life: { emoji: '❤️', points: 15, color: 'pink', probability: 0.1 }
      }
  
      // 扩展食物属性
      const food = reactive({ 
        x: 10, 
        y: 10,
        type: 'normal'
      })
  
      // 皮肤配置
      const skins = {
        classic: {
          color: 'green',
          gradient: false
        },
        fire: {
          color: 'orange',
          gradient: true,
          gradientColors: ['#ff4e50', '#f9d423']
        },
        rainbow: {
          color: 'purple',
          gradient: true,
          gradientColors: ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
        }
      }
  
      // 蛇的身体部分，使用 reactive 创建响应式数组对象
      const snake = reactive([{ x: 5, y: 5 }])
      // 蛇的移动方向，使用 ref 创建响应式数据
      const direction = ref('right')
      // 玩家的得分，使用 ref 创建响应式数据
      const score = ref(0)
      // 玩家的剩余生命，使用 ref 创建响应式数据
      const lives = ref(3)
      // 游戏循环的定时器 ID，使用 ref 创建响应式数据
      const gameLoop = ref(null)
  
      // ============= 计算属性 =============
      // 计算游戏棋盘的样式，返回一个对象，包含宽度、高度、边框等样式属性
      // 1. computed 函数的使用：computed 是 Vue 框架中提供的一个函数，它用于创建计算属性。计算属性是一种基于其他响应式数据（如响应式对象的属性、响应式数组等）的衍生状态。
      // 计算属性具有缓存机制，只有当它所依赖的响应式数据发生变化时，才会重新计算其值，否则会直接返回缓存的结果。这有助于提高性能，避免不必要的重复计算。
      // 2. 传递给 computed 函数的是一个箭头函数 () => ({ })。这个箭头函数的作用是定义计算属性 boardStyle 的计算逻辑。
      const boardStyle = computed(() => ({
        width: `${gridSize.value * 30}px`,
        height: `${gridSize.value * 30}px`,
        border: '2px solid #333',
        position: 'relative',
        margin: '20px auto'
      }))
  
      // 计算食物的位置样式，返回一个对象，包含食物的 left 和 top 位置属性
      // 1. computed 函数的使用：computed 是 Vue 框架中提供的一个函数，它用于创建计算属性。计算属性是一种基于其他响应式数据（如响应式对象的属性、响应式数组等）的衍生状态。
      // 计算属性具有缓存机制，只有当它所依赖的响应式数据发生变化时，才会重新计算其值，否则会直接返回缓存的结果。这有助于提高性能，避免不必要的重复计算。
      // 2. 传递给 computed 函数的是一个箭头函数 () => ({ left: \({food.x * 30}px`, top: `\){food.y * 30}px })。这个箭头函数的作用是定义计算属性 foodPosition 的计算逻辑。
      // 3. 计算属性 foodPosition 的返回值是一个包含 left 和 top 属性的对象，这个对象可以用于在 Vue 模板中绑定内联样式（例如通过 :style 指令），从而动态地设置食物在页面上的位置。当 food 对象的 x 或 y 属性发生变化时（因为 food 是响应式的），foodPosition 计算属性会自动重新计算，进而更新食物在页面上的显示位置。
      // 4. ${} 是 JavaScript 中模板字符串的语法。在模板字符串中，${} 被用来嵌入表达式。具体来说，你可以把任意有效的 JavaScript 表达式放在 ${} 内部，JavaScript 会先计算该表达式的值，然后将其结果插入到模板字符串里对应的位置。
      const foodPosition = computed(() => ({
        left: `${food.x * 30}px`,
        top: `${food.y * 30}px`
      }))
  
      // ============= 游戏方法 =============
      // 生成食物的方法
      const spawnFood = () => {
        food.x = Math.floor(Math.random() * gridSize.value)
        food.y = Math.floor(Math.random() * gridSize.value)
        
        // 随机选择食物类型
        const rand = Math.random()
        let cumProb = 0
        for (const [type, config] of Object.entries(foodTypes)) {
          cumProb += config.probability
          if (rand <= cumProb) {
            food.type = type
            break
          }
        }
        
        // 确保食物不会出现在蛇身上
        while (snake.some(seg => seg.x === food.x && seg.y === food.y)) {
          food.x = Math.floor(Math.random() * gridSize.value)
          food.y = Math.floor(Math.random() * gridSize.value)
        }
      }
  
      // 检查碰撞的方法，判断传入的位置是否与边界或蛇自身碰撞
      const checkCollision = (pos) => {
        return (
          pos.x < 0 || 
          pos.x >= gridSize.value || 
          pos.y < 0 || 
          pos.y >= gridSize.value ||
          snake.some(seg => seg.x === pos.x && seg.y === pos.y)
        )
      }
  
      // 移动蛇的方法，根据当前方向移动蛇头，处理碰撞、吃食物等逻辑
      const move = () => {
        const head = { ...snake[0] }
        
        switch(direction.value) {
          case 'up': head.y--; break
          case 'down': head.y++; break
          case 'left': head.x--; break
          case 'right': head.x++; break
        }
  
        if (checkCollision(head)) {
          lives.value--
          if (lives.value <= 0) {
            // 更新排行榜
            leaderboard.value.push(score.value)
            leaderboard.value.sort((a, b) => b - a)
            leaderboard.value = leaderboard.value.slice(0, 10)
            
            alert(`游戏结束！最终得分: ${score.value}`)
            startGame()
          }
          return
        }
  
        if (head.x === food.x && head.y === food.y) {
          handleFoodEffect()
          spawnFood()
        } else {
          snake.pop()
        }
  
        snake.unshift(head)
      }
  
      // 开始游戏的方法，重置游戏状态并启动游戏循环
      const startGame = () => {
        clearInterval(gameLoop.value)
        snake.splice(0, snake.length, { x: 5, y: 5 })
        direction.value = 'right'
        score.value = 0
        lives.value = 3
        spawnFood()
        gameLoop.value = setInterval(move, 200)
      }
  
      // 改变蛇移动方向的方法，确保不会反向移动
      const changeDirection = (newDir) => {
        const opposite = {
          up: 'down', down: 'up',
          left: 'right', right: 'left'
        }
        if (newDir !== opposite[direction.value]) {
          direction.value = newDir
        }
      }
  
      // 获取蛇每一段样式的方法，返回一个对象，包含位置、背景颜色等样式属性
      const getSegmentStyle = (segment, index) => {
        const skin = skins[gameConfig.skinType]
        let backgroundColor = skin.color
        
        if (skin.gradient) {
          const gradientIndex = index % skin.gradientColors.length
          backgroundColor = skin.gradientColors[gradientIndex]
        }
        
        return {
          left: `${segment.x * 30}px`,
          top: `${segment.y * 30}px`,
          background: backgroundColor,
          width: '28px',
          height: '28px',
          position: 'absolute',
          borderRadius: '4px',
          transition: 'all 0.1s ease-out'
        }
      }
  
      // 更新难度
      const updateDifficulty = () => {
        const speeds = {
          easy: 300,
          medium: 200,
          hard: 100
        }
        gameConfig.baseSpeed = speeds[gameConfig.difficulty]
        if (gameLoop.value) {
          clearInterval(gameLoop.value)
          gameLoop.value = setInterval(move, gameConfig.baseSpeed / gameConfig.speedMultiplier)
        }
      }
  
      // 处理食物效果
      const handleFoodEffect = () => {
        const foodConfig = foodTypes[food.type]
        score.value += foodConfig.points
        
        switch (food.type) {
          case 'speed':
            gameConfig.speedMultiplier = 2
            setTimeout(() => {
              gameConfig.speedMultiplier = 1
              updateDifficulty()
            }, 2000)
            break
          case 'slow':
            gameConfig.speedMultiplier = 0.5
            setTimeout(() => {
              gameConfig.speedMultiplier = 1
              updateDifficulty()
            }, 2000)
            break
          case 'life':
            lives.value = Math.min(lives.value + 1, 5)
            break
        }
        
        updateDifficulty()
      }
  
      // 计算前10名分数
      const topScores = computed(() => {
        return leaderboard.value.slice(0, 10)
      })
  
      // ============= 生命周期 =============
      // 组件挂载完成后的钩子函数，启动游戏并绑定键盘事件
      onMounted(() => {
        startGame()
        
        window.addEventListener('keydown', (e) => {
          const dirMap = {
            ArrowUp: 'up',
            ArrowDown: 'down',
            ArrowLeft: 'left',
            ArrowRight: 'right'
          }
          if (dirMap[e.key]) changeDirection(dirMap[e.key])
        })
      })
  
      // 组件卸载前的钩子函数，清除游戏循环定时器
      onBeforeUnmount(() => {
        clearInterval(gameLoop.value)
      })
  
      // ============= 暴露给模板 =============
      // 将需要在模板中使用的状态、计算属性和方法返回
      return {
        // 状态
        snake,
        score,
        lives,
        food,
        // 计算属性
        boardStyle,
        foodPosition,
        // 方法
        startGame,
        changeDirection,
        getSegmentStyle,
        gameConfig,
        topScores,
        updateDifficulty,
        foodTypes
      }
    }
  }
  </script>
  
  <style scoped>
  .game-container {
    font-family: 'Arial', sans-serif;
    text-align: center;
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
    background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  }
  
  .status {
    margin-bottom: 20px;
    font-size: 1.2em;
    color: #2c3e50;
    padding: 10px;
    background: rgba(255, 255, 255, 0.8);
    border-radius: 5px;
  }
  
  .game-settings {
    display: flex;
    justify-content: center;
    gap: 20px;
    margin-bottom: 20px;
  }
  
  .difficulty-selector,
  .skin-selector {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  
  select {
    padding: 8px;
    border-radius: 5px;
    border: 1px solid #ddd;
    background: white;
    cursor: pointer;
  }
  
  .game-board {
    background: rgba(255, 255, 255, 0.9);
    border-radius: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
  
  .snake-segment {
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    transform-origin: center;
  }
  
  .food {
    position: absolute;
    font-size: 24px;
    transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
    animation: foodAppear 0.5s ease-out;
  }
  
  @keyframes foodAppear {
    from {
      transform: scale(0);
      opacity: 0;
    }
    to {
      transform: scale(1);
      opacity: 1;
    }
  }
  
  .controls {
    margin-top: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 15px;
  }
  
  .start-btn {
    padding: 12px 24px;
    font-size: 1.1em;
    background: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s;
  }
  
  .start-btn:hover {
    background: #45a049;
    transform: translateY(-2px);
  }
  
  .arrows {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
  }
  
  .horizontal-arrows {
    display: flex;
    gap: 20px;
  }
  
  .arrow-btn {
    width: 50px;
    height: 50px;
    font-size: 1.5em;
    background: #2196F3;
    color: white;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  
  .arrow-btn:hover {
    background: #1976D2;
    transform: scale(1.1);
  }
  
  .leaderboard {
    margin-top: 20px;
    padding: 15px;
    background: rgba(255, 255, 255, 0.8);
    border-radius: 10px;
  }
  
  .leaderboard h3 {
    color: #2c3e50;
    margin-bottom: 10px;
  }
  
  .scores-list {
    display: flex;
    flex-direction: column;
    gap: 5px;
  }
  
  .score-item {
    display: flex;
    justify-content: space-between;
    padding: 5px 10px;
    background: rgba(255, 255, 255, 0.5);
    border-radius: 5px;
  }
  
  .rank {
    color: #666;
    font-weight: bold;
  }
  
  .score-value {
    color: #2196F3;
    font-weight: bold;
  }
  </style>